/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering,
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 *
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.service.replay.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import cgl.narada.service.replay.ReplayRequest;
import cgl.narada.service.replay.ReplayResponse;
import cgl.narada.service.replay.ReplayServiceExchange;

/**
 A replay response that snapshots the response to a prior replay request.
 This provides information regarding whether the earlier request was
 successful, and if not, information regarding the cause of the failure.

 @author Hasan Bulut and Shrideep Pallickara
 $Date$
 $Revision$
 */

public class ReplayResponseEvent implements ReplayResponse {
  private int     templateId;
  private int     entityId;

  private boolean hasRequestIdentifier          = false;
  private String  replayRequestIdentifier       = null;

  private boolean hasListOfSequencesToReplay    = false;
  private long[]  listOfSequencesToReplay       = null;
  private boolean hasReplayTopic                = false;                  // added for rtsp
  private String  replayTopic                   = null;                   // added for rtsp
  private boolean success                       = true;

  boolean         hasAdditionalInfo             = false;
  private String  additionalInfo                = null;

  private String  moduleName                    = "ReplayResponseEvent: ";

  private boolean hasSequenceAndTimestampValues = false;
  private long[]  listOfSequences               = null;
  private long[]  listOfTimestamps              = null;

  private boolean hasRequestedTimestamp         = false;                  // added for rtsp
  private long    sequenceNumber                = 0;                      // added for rtsp
  private long    requestedTimestamp            = 0;                      // added for rtsp

  public ReplayResponseEvent(int templateId, int entityId,
      String replayRequestIdentifier, boolean success, String additionalInfo,
      long[] listOfSequencesToReplay) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }

    this.success = success;

    this.additionalInfo = additionalInfo;
    if (this.additionalInfo != null) {
      hasAdditionalInfo = true;
    }

    this.listOfSequencesToReplay = listOfSequencesToReplay;
    if (listOfSequencesToReplay != null) {
      hasListOfSequencesToReplay = true;
    }
  }

  public ReplayResponseEvent(byte[] marshalledBytes) {
    ByteArrayInputStream baInputStream = new ByteArrayInputStream(
        marshalledBytes);
    DataInputStream din = new DataInputStream(baInputStream);

    try {
      din.readByte(); /*ReplayServiceExchange.REPLAY_SERVICE_RESPONSE*/

      templateId = din.readInt();
      entityId = din.readInt();

      /** Deal with request identifier */
      hasRequestIdentifier = din.readBoolean();
      if (hasRequestIdentifier) {
        byte[] idBytes = new byte[din.readInt()];
        din.readFully(idBytes);
        replayRequestIdentifier = new String(idBytes);
      }

      hasReplayTopic = din.readBoolean();
      if (hasReplayTopic) {
        byte[] tpcBytes = new byte[din.readInt()];
        din.readFully(tpcBytes);
        replayTopic = new String(tpcBytes);
      }

      /** Deal with specific sequences */
      hasListOfSequencesToReplay = din.readBoolean();
      if (hasListOfSequencesToReplay) {
        int numOfSequences = din.readInt();
        listOfSequencesToReplay = new long[numOfSequences];
        for (int i = 0; i < numOfSequences; i++) {
          listOfSequencesToReplay[i] = din.readLong();
        }
      }

      success = din.readBoolean();

      boolean hasAdditionalInfo = din.readBoolean();
      if (hasAdditionalInfo) {
        byte[] _infoBytes = new byte[din.readInt()];
        din.readFully(_infoBytes);
        additionalInfo = new String(_infoBytes);
      }

      hasSequenceAndTimestampValues = din.readBoolean();
      if(hasSequenceAndTimestampValues) {
        int numOfSequences = din.readInt();
        listOfSequences = new long[numOfSequences];
        for (int i = 0; i < numOfSequences; i++) {
          listOfSequences[i] = din.readLong();
        }
        int numOfTimestamps = din.readInt();
        listOfTimestamps = new long[numOfTimestamps];
        for (int i = 0; i < numOfTimestamps; i++) {
          listOfTimestamps[i] = din.readLong();
        }
      }

      hasRequestedTimestamp = din.readBoolean();
      if (hasRequestedTimestamp) {
        sequenceNumber = din.readLong();
        requestedTimestamp = din.readLong();
      }

    } catch (IOException ioe) {
      System.out.println(moduleName + "Error while un-marshalling");
    }
    /** end try-catch block*/

  }

  public byte[] getBytes() {
    ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(baOutputStream);
    byte[] marshalledBytes = null;
    try {
      dout.writeByte(ReplayServiceExchange.REPLAY_SERVICE_RESPONSE);

      dout.writeInt(templateId);
      dout.writeInt(entityId);

      /** Deal with request identifier */
      dout.writeBoolean(hasRequestIdentifier);
      if (hasRequestIdentifier) {
        byte[] idBytes = replayRequestIdentifier.getBytes();
        dout.writeInt(idBytes.length);
        dout.write(idBytes);
      }

      dout.writeBoolean(hasReplayTopic);
      if (replayTopic != null) {
        byte[] tpcBytes = replayTopic.getBytes();
        dout.writeInt(tpcBytes.length);
        dout.write(tpcBytes);
      }

      /** Deal with specific sequences */
      dout.writeBoolean(hasListOfSequencesToReplay);
      if (hasListOfSequencesToReplay) {
        int numOfSequencesToReplay = listOfSequencesToReplay.length;
        dout.writeInt(numOfSequencesToReplay);
        for (int i = 0; i < numOfSequencesToReplay; i++) {
          dout.writeLong(listOfSequencesToReplay[i]);
        }
      }

      dout.writeBoolean(success);

      if (additionalInfo != null) {
        dout.writeBoolean(true);
        dout.writeInt(additionalInfo.length());
        dout.write(additionalInfo.getBytes());
      } else {
        dout.writeBoolean(false);
      }

      dout.writeBoolean(this.hasSequenceAndTimestampValues);
      if (hasSequenceAndTimestampValues) {
        int numOfSequences = listOfSequences.length;
        dout.writeInt(numOfSequences);
        for (int i = 0; i < numOfSequences; i++) {
          dout.writeLong(listOfSequences[i]);
        }

        int numOfTimestamps = listOfTimestamps.length;
        dout.writeInt(numOfTimestamps);
        for (int i = 0; i < numOfTimestamps; i++) {
          dout.writeLong(listOfTimestamps[i]);
        }
      }

      dout.writeBoolean(hasRequestedTimestamp);
      if (hasRequestedTimestamp) {
        dout.writeLong(sequenceNumber);
        dout.writeLong(requestedTimestamp);
      }

      marshalledBytes = baOutputStream.toByteArray();
    } catch (IOException ioe) {
      System.out.println(moduleName + "Error while marshalling");
    }
    /** end try-catch block*/

    return marshalledBytes;
  }

  private void setCommonRequestResponseFields(ReplayRequest replayRequest) {

    this.templateId = replayRequest.getTemplateId();
    this.entityId = replayRequest.getEntityId();

    this.replayRequestIdentifier = replayRequest.getReplayIdentifier();
    if (this.replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }

    this.replayTopic = replayRequest.getReplayTopic();
    if (this.replayTopic != null) {
      this.hasReplayTopic = true;
    }
  }

  private void setCommonResponseFields(boolean success, String additionalInfo) {
    this.success = success;
    this.additionalInfo = additionalInfo;
    if (this.additionalInfo != null) {
      hasAdditionalInfo = true;
    }
  }

  public ReplayResponseEvent(ReplayRequest replayRequest, boolean success,
      String additionalInfo, long[] listOfSequencesToReplay) {

    setCommonRequestResponseFields(replayRequest);
    setCommonResponseFields(success, additionalInfo);

    this.listOfSequencesToReplay = listOfSequencesToReplay;
    if (this.listOfSequencesToReplay != null) {
      hasListOfSequencesToReplay = true;
    }

  }

  public ReplayResponseEvent(ReplayRequest replayRequest, boolean success,
      String additionalInfo, boolean hasSequenceAndTimestampValues,
      long[] sequences, long[] timestamps) {

    setCommonRequestResponseFields(replayRequest);
    setCommonResponseFields(success, additionalInfo);

    this.hasSequenceAndTimestampValues = hasSequenceAndTimestampValues;
    if (this.hasSequenceAndTimestampValues) {
      this.listOfSequences = sequences;
      this.listOfTimestamps = timestamps;
      System.out.println("seq. length:"+listOfSequences.length);
      System.out.println("ts. length:"+listOfTimestamps.length);
    }
  }

  public ReplayResponseEvent(ReplayRequest replayRequest, boolean success,
      String additionalInfo, boolean hasRequestedTimestamp,
      long sequenceNumber, long requestedTimestamp) {

    setCommonRequestResponseFields(replayRequest);
    setCommonResponseFields(success, additionalInfo);

    this.hasRequestedTimestamp = hasRequestedTimestamp;
    if (hasRequestedTimestamp) {
      this.sequenceNumber = sequenceNumber;
      this.requestedTimestamp = requestedTimestamp;
    }

  }

  public int getTemplateId() {
    return templateId;
  }

  public int getEntityId() {
    return entityId;
  }

  public String getReplayIdentifier() {
    return replayRequestIdentifier;
  }

  public boolean hasReplayTopic() {
    return hasReplayTopic;
  }

  public String getReplayTopic() {
    return replayTopic;
  }

  public boolean isSuccess() {
    return success;
  }

  public boolean hasAdditionalInfo() {
    return hasAdditionalInfo;
  }

  public String getAdditionalInfo() {
    return additionalInfo;
  }

  public boolean hasListOfSequencesToReplay() {
    return hasListOfSequencesToReplay;
  }


	public boolean hasRequestedTimestamp() {
		return this.hasRequestedTimestamp;
	}
	public long getRequestedTimestamp() {
		return this.requestedTimestamp;
	}
	public long getSequenceNumber() {
		return this.sequenceNumber;
	}
  public long[] getSequenceInfoOfEventsToBeReplayed() {
    return listOfSequencesToReplay;
  }

  public boolean hasSequenceAndTimestampValues() {
    return hasSequenceAndTimestampValues;
  }

  public long[] getListOfSequences() {
    return listOfSequences;
  }

  public long[] getListOfTimestamps() {
    return listOfTimestamps;
  }

  public String toString() {
    String stringRep = "\n";
    stringRep = "templateId=" + templateId;
    stringRep +="\nentityId=" + entityId;
    stringRep +="\nhasReplayIdentifier="+this.hasRequestIdentifier;
    stringRep +="\nreplayRequestIdentifier=" + replayRequestIdentifier;
    stringRep +="\nhasReplayTopic="+hasReplayTopic;
    stringRep +="\nreplayTopic="+replayTopic;
    stringRep +="\nhasListOfSequencesToReplay="+hasListOfSequencesToReplay;
    if(listOfSequencesToReplay != null)
      stringRep +="\n\tlength:"+listOfSequencesToReplay.length;
    stringRep +="\nsuccess="+success;
    stringRep +="\nadditionalInfos="+additionalInfo;
    stringRep +="\nhasSequencesAndTimestampValues="+this.hasSequenceAndTimestampValues;
    if(listOfSequences != null)
      stringRep +="\n\tlength:"+listOfSequences.length;
    if(listOfTimestamps != null)
      stringRep +="\n\tlength:"+listOfTimestamps.length;
    stringRep +="\nhasRequestedTimestamp="+this.hasRequestedTimestamp;
    stringRep +="\nsequenceNumber="+sequenceNumber;
    stringRep +="\ntimestamp="+requestedTimestamp;
    return stringRep;
  }

}
